// 蜂窝蜜造平台生成代码，如手工更改，请添加到 .beeignore 忽略生成

package com.fowo.api.documentCirculation.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fowo.api.common.model.ImportRow;
import com.fowo.api.common.model.RException;
import com.fowo.api.common.util.ResponseUtils;
import com.fowo.api.common.util.SheetUtils;
import com.fowo.api.documentCirculation.entity.DataTask;
import com.fowo.api.documentCirculation.mapper.DataTaskMapper;
import com.fowo.api.documentCirculation.model.*;
import com.fowo.api.documentCirculation.model.constant.DataTaskTypeEnums;
import com.fowo.api.documentCirculation.service.DataTaskBranchService;
import com.fowo.api.documentCirculation.service.DataTaskMappingService;
import com.fowo.api.documentCirculation.service.DataTaskService;
import com.fowo.api.sys.entity.SysFile;
import com.fowo.api.sys.entity.SysMenu;
import com.fowo.api.sys.mapper.SysMenuMapper;
import com.fowo.api.sys.model.NewSerialRequest;
import com.fowo.api.sys.service.FileService;
import com.fowo.api.sys.service.SysSerialService;
import com.fowo.api.user.model.JwtUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 数据事务 服务实现类
 * @author yl_ls
 */
@Service
@Slf4j
@DS("ds30")
public class DataTaskServiceImpl
  extends ServiceImpl<DataTaskMapper, DataTask>
  implements DataTaskService {

  public static final Map<String, String> SORT_FIELD_MAP = new HashMap<>();
  // 导出时单次查询最大记录数
  protected static final int EXPORT_QUERY_MAX_SIZE = 1000;
  // 支持导出的最大记录数(1048576 为 Excel 支持的最大行数)
  protected static final long EXPORT_MAX_SIZE = 1048575;

  @Resource
  protected DataTaskBranchService dataTaskBranchService;

  @Resource
  protected SysSerialService sysSerialService;

  @Resource
  protected FileService fileService;

  @Resource
  protected DataTaskBranchService branchService;

  @Resource
  private DataTaskMappingService mappingService;

  @Resource
  private SysMenuMapper sysMenuMapper;

  @Resource
  protected Validator validator;

  /**
   * 创建数据事务
   * @param model 数据事务
   * @return 新数据的主键
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  public Long create(DataTaskVo model) throws Exception {
    JwtUserInfo currentUser = JwtUserInfo.fromHeader();
    DataTask entity = new DataTask();
    BeanUtils.copyProperties(model, entity);
    entity.setCreateTime(new Date());
    entity.setLastUpdateTime(new Date());
    if (currentUser != null) {
      entity.setCreateUser(currentUser.getUserId());
      entity.setLastUpdateUser(currentUser.getUserId());
    }
    // 填充序列号
    if (!StringUtils.hasText(entity.getCode())) {
      entity.setCode(
        sysSerialService.getNewSerial(
          new NewSerialRequest(null, "DataTask.code")
        )
      );
    }
    if (this.save(entity)) {
      model.setId(entity.getId());
      updateAllChilds(model);
      updateSysMenu(model);
      return entity.getId();
    }
    throw new Exception("数据事务保存失败");
  }

  /**
   * 获取数据任务编号对应的菜单权限标识(下推)
   * @param dataTaskId 数据任务编号
   * @return 菜单权限标识
   */
  private String getSysMenuAuthority(Long dataTaskId) {
    return String.format("dataTask_%d", dataTaskId);
  }

  /**
   * 获取数据任务编号对应的菜单权限标识(勾)
   * @param dataTaskId 数据任务编号
   * @return 菜单权限标识
   */
  private String getPullSysMenuAuthority(Long dataTaskId) {
    return String.format("dataTaskPull_%d", dataTaskId);
  }

  private void updateSysMenu(DataTaskVo vo) {
    updateOrDeleteSysMenu(vo, getSysMenuAuthority(vo.getId()), "单据下推", !(vo.getType().contains("1") || vo.getType().contains("2")), vo.getButton1Props() != null ? vo.getButton1Props() : vo.getButton2Props());
    updateOrDeleteSysMenu(vo, getPullSysMenuAuthority(vo.getId()), "单据勾稽", !vo.getType().contains("3"), vo.getButton3Props());
  }

  private String getSysMenuName(DataTaskVo vo, String titlePrefix, DataTaskButtonProps buttonProps) {
    String menuName = String.format("%s - %s", titlePrefix, vo.getName());
    if (buttonProps != null && StringUtils.hasText(buttonProps.getTitle())) {
      menuName = buttonProps.getTitle();
    }
    return menuName;
  }

  /**
   * 自动创建或更新事务对应的系统菜单
   * @param vo)
   */
  private void updateOrDeleteSysMenu(DataTaskVo vo, String authority, String titlePrefix, boolean isRemove , DataTaskButtonProps buttonProps) {
    SysMenu sysMenu = sysMenuMapper.selectOne(Wrappers.<SysMenu>query().eq("authority", authority));
    if (sysMenu == null) {
      if (isRemove) {
        sysMenuMapper.deleteById(sysMenu);
        return;
      }
      String mainFromTableName = vo.getMainBranch().getFromTable();
      String parentAuthority = StringUtils.hasText(vo.getMainBranch().getFromObjectName()) ? vo.getMainBranch().getFromObjectName() : tableNameToObjectAuthority(mainFromTableName);
      SysMenu parentSysMenu = sysMenuMapper.selectOne(Wrappers.<SysMenu>query().eq("authority", parentAuthority));
      // 创建新的系统菜单
      sysMenu = new SysMenu();
      if (parentSysMenu != null) {
        sysMenu.setParentMenuId(parentSysMenu.getId());
      }
      sysMenu.setMenuType("Action");
      sysMenu.setMenuName(getSysMenuName(vo, titlePrefix, buttonProps));
      sysMenu.setSort(10000);
      sysMenu.setAuthority(authority);
      if(sysMenuMapper.insert(sysMenu) == 0) {
        throw new RException("创建按钮权限节点异常");
      }
    } else {
      if (isRemove) {
        return;
      }
      SysMenu updateSysMenu = new SysMenu();
      updateSysMenu.setId(sysMenu.getId());
      updateSysMenu.setMenuName(getSysMenuName(vo, titlePrefix, buttonProps));
      if(sysMenuMapper.updateById(updateSysMenu) == 0) {
        throw new RException("更新按钮权限节点异常");
      }
    }
  }

  private String tableNameToObjectAuthority(String tableName) {
    final Pattern pattern = Pattern.compile("_+[a-z]");
    final Matcher matcher = pattern.matcher(tableName);
    if (matcher.matches()) {
      StringBuffer sb = new StringBuffer();
      while (matcher.find()) {
        matcher.appendReplacement(sb, matcher.group(0));
      }
      matcher.appendTail(sb);
      return sb.toString();
    }
    return tableName;
  }

  private void removeSysMenu(Long dataTaskId) {
    String authority = getSysMenuAuthority(dataTaskId);
    sysMenuMapper.delete(Wrappers.<SysMenu>query().eq("authority", authority));
    String authority2 = getPullSysMenuAuthority(dataTaskId);
    sysMenuMapper.delete(Wrappers.<SysMenu>query().eq("authority", authority2));
  }

  private void removeSysMenus(List<Long> dataTaskIds) {
    List<String> authorityList = dataTaskIds.stream().map(this::getSysMenuAuthority).collect(Collectors.toList());
    if (!authorityList.isEmpty()) {
      sysMenuMapper.delete(Wrappers.<SysMenu>query().in("authority", authorityList));
    }
    List<String> authorityList2 = dataTaskIds.stream().map(this::getPullSysMenuAuthority).collect(Collectors.toList());
    if (!authorityList2.isEmpty()) {
      sysMenuMapper.delete(Wrappers.<SysMenu>query().in("authority", authorityList2));
    }
  }

  /**
   * 更新所有子表
   * @param model 数据事务
   */
  protected void updateAllChilds(DataTaskVo model) throws Exception {
    if (model.getBranch() != null) {
      dataTaskBranchService.saveAllByParentId(model.getId(), model.getBranch());
    } else if(model.getMainBranch() != null) {
      DataTaskBranchVo branchVo = new DataTaskBranchVo();
      BeanUtils.copyProperties(model.getMainBranch(), branchVo);
      branchVo.setParentId(model.getId());
      if (model.getMainBranch().getId() == null) {
        dataTaskBranchService.create(branchVo);
      } else {
        dataTaskBranchService.update(branchVo);
      }
    }
  }

  /**
   * 更新数据事务
   * @param model 数据事务
   * @return 更新是否成功
   */
  @Override
  @Transactional(rollbackFor = Exception.class)
  public boolean update(DataTaskVo model) throws Exception {
    JwtUserInfo currentUser = JwtUserInfo.fromHeader();
    DataTask entity = this.getById(model.getId());
    if (entity == null) {
      throw new Exception("数据事务不存在");
    }
    BeanUtils.copyProperties(
      model,
      entity,
      "id",
      "createTime",
      "createUser",
      "lastUpdateTime",
      "lastUpdateUser"
    );
    entity.setLastUpdateTime(new Date());
    if (currentUser != null) {
      entity.setLastUpdateUser(currentUser.getUserId());
    }
    if (this.updateById(entity)) {
      updateAllChilds(model);
      updateSysMenu(model);
      return true;
    }
    return false;
  }

  /**
   * 更新数据事务（带空值）
   * @param model 数据事务
   * @return 更新是否成功
   */
  @Override
  @Transactional(rollbackFor = Exception.class)
  public boolean updateForEdit(DataTaskVo model) throws Exception {
    JwtUserInfo currentUser = JwtUserInfo.fromHeader();
    DataTask entity = this.getById(model.getId());
    if (entity == null) {
      throw new Exception("数据事务不存在");
    }
    model.setLastUpdateTime(new Date());
    if (currentUser != null) {
      model.setLastUpdateUser(currentUser.getUserId());
    }
    boolean isOk = new LambdaUpdateChainWrapper<>(baseMapper)
      .set(DataTask::getCode, model.getCode())
      .set(DataTask::getName, model.getName())
      .set(DataTask::getType, model.getType())
      .set(DataTask::getLastUpdateTime, model.getLastUpdateTime())
      .set(DataTask::getLastUpdateUser, model.getLastUpdateUser())
      .eq(DataTask::getId, model.getId())
      .update();
    if (isOk) {
      updateAllChilds(model);
      return true;
    }
    return false;
  }

  /**
   * 删除数据事务
   * @param id 数据事务的主键
   * @return 删除是否成功
   */
  @Override
  public boolean delete(Long id) throws Exception {
    if (this.removeById(id)){
      removeSysMenu(id);
      return true;
    }
    return false;
  }

  /**
   * 批量删除数据事务
   * @param ids 数据事务的主键列表
   * @return 删除是否成功
   */
  @Override
  public boolean batchDelete(List<Long> ids) throws Exception {
    if(baseMapper.deleteBatchIds(ids) > 0) {
      removeSysMenus(ids);
      return true;
    }
    return false;
  }

  /**
   * 导入单行处理
   * @param row 要导入的行
   * @param rowNumber 所在行号
   * @param vo 输出VO,可空
   * @param variables 变量池，用于在一次导入过程中保存临时信息
   * @return 导入行对象
   */
  protected ImportRow<DataTaskImportPo> excelToImportRow(
    DataTaskImportPo row,
    Integer rowNumber,
    DataTaskVo vo,
    Map<String, Object> variables
  ) {
    ImportRow<DataTaskImportPo> ir = new ImportRow<DataTaskImportPo>();
    ir.setRowNumber(rowNumber);
    ir.setRow(row);
    if (SheetUtils.isAllFieldsEmpty(row)) {
      ir.setEmptyRow(true);
      ir.addError("", "空行");
      return ir;
    }
    final Set<ConstraintViolation<DataTaskImportPo>> validate = validator.validate(
      row
    );
    boolean isOutputVo = vo != null;
    if (!isOutputVo) {
      vo = new DataTaskVo();
    }
    BeanUtils.copyProperties(row, vo);
    // 收集基本校验错误
    ir.initErrors(validate, DataTaskImportPo.class);

    // 一般数据处理
    if (StringUtils.hasText(row.getType())) {
      vo.setType(DataTaskTypeEnums.toValue(row.getType()));
      if (vo.getType() == null) {
        ir.addError("type", "类型只能是：“整单下推”, “自选下推”, “下游钩稽”");
      }
    }

    return ir;
  }

  /**
   * 导入预览
   * @param sysFile 已上传到系统的文件
   */
  @Override
  public List<ImportRow<DataTaskImportPo>> importPreview(SysFile sysFile)
    throws Exception {
    Map<String, Object> variables = new HashMap<>();
    try (InputStream in = fileService.read(sysFile)) {
      List<ImportRow<DataTaskImportPo>> rows = new ArrayList<>();

      // 读取 Excel 到 rows
      EasyExcel
        .read(
          in,
          DataTaskImportPo.class,
          new ReadListener<DataTaskImportPo>() {
            @Override
            public void invoke(DataTaskImportPo row, AnalysisContext cxt) {
              ImportRow<DataTaskImportPo> ir = excelToImportRow(
                row,
                cxt.readRowHolder().getRowIndex(),
                null,
                variables
              );
              if (ir.isEmptyRow()) {
                return;
              }
              rows.add(ir);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {}
          }
        )
        .sheet()
        .doRead();

      return rows;
    }
  }

  /**
   * 完成导入
   * @param rows 预导入的行信息
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  public void importDone(List<ImportRow<DataTaskImportPo>> rows)
    throws Exception {
    Map<String, Object> variables = new HashMap<>();
    for (int i = 0; i < rows.size(); i++) {
      ImportRow<DataTaskImportPo> row = rows.get(i);
      DataTaskVo vo = new DataTaskVo();
      row = excelToImportRow(row.getRow(), row.getRowNumber(), vo, variables);
      if (row.getErrors() != null && row.getErrors().size() > 0) {
        throw new RException(
          String.format(
            "第%d行（Excel第%d行）数据验证错误：%s",
            i + 1,
            row.getRowNumber(),
            row.getJoinErrorMessage()
          )
        );
      }
      create(vo);
    }
  }

  /**
   * 导出
   * @param search 查询条件
   */
  @Override
  public void export(
    DataTaskSearchParamPo search,
    HttpServletResponse response
  ) throws Exception {
    search.setPageSize(EXPORT_QUERY_MAX_SIZE);
    int current = 1;
    List<DataTaskExcelPo> items = new ArrayList<DataTaskExcelPo>();
    while (true) {
      search.setCurrent(current);
      Page<DataTaskItemVo> page = this.pageSearch(search);
      if (page.getTotal() > EXPORT_MAX_SIZE) {
        throw new RException("导出记录数超出限制！");
      }
      List<DataTaskItemVo> list = page.getRecords();
      if (list.isEmpty()) {
        break;
      }
      for (DataTaskItemVo item : list) {
        DataTaskExcelPo excel = new DataTaskExcelPo();
        BeanUtils.copyProperties(item, excel);

        items.add(excel);
      }
      if (list.size() < EXPORT_QUERY_MAX_SIZE) {
        break;
      }
      current++;
    }

    ResponseUtils.setAttachmentFileName(response, "数据事务.xlsx");
    EasyExcel
      .write(response.getOutputStream())
      .sheet()
      .head(DataTaskExcelPo.class)
      .doWrite(items);
  }

  /**
   * 通过名称查询数据事务主键(重复时返回最新的主键)
   */
  @Override
  public Long getIdByName(String name) {
    return this.baseMapper.getIdByName(name);
  }

  /**
   * 通过名称查询数据事务多主键(重复时返回最新的主键)
   */
  @Override
  public List<Long> getIdsByName(List<String> list) {
    return this.baseMapper.getIdsByName(list);
  }

  /**
   * 通过数据事务主键查询名称
   */
  @Override
  public String getNameById(Long id) {
    return this.baseMapper.getNameById(id);
  }

  /**
   * 通过数据事务主键查询名称列表
   */
  @Override
  public List<String> getNameByIds(List<Long> ids) {
    return this.baseMapper.getNameByIds(ids);
  }

  /**
   * 获取详情
   * @param id 数据事务的主键
   */
  @Override
  public DataTaskVo getVoById(Long id) throws Exception {
    DataTaskVo vo = this.baseMapper.selectVoById(id);
    if (vo == null) {
      return null;
    }

    DataTaskBranchSearchParamPo branchSearchParamPo = new DataTaskBranchSearchParamPo();
    branchSearchParamPo.setParentId(id);
    branchSearchParamPo.setDisableDataPremissions(true);
    vo.setBranch(dataTaskBranchService.search(branchSearchParamPo));

    for (DataTaskBranchItemVo branch : vo.getBranch()) {
      DataTaskMappingSearchParamPo mappingSearchParamPo = new DataTaskMappingSearchParamPo();
      mappingSearchParamPo.setDisableDataPremissions(true);
      mappingSearchParamPo.setBranchId(branch.getId());
      branch.setMappings(mappingService.search(mappingSearchParamPo));
    }

    return vo;
  }

  @Override
  public DataTaskVo getVoByCode(String code) throws Exception {
    DataTaskVo vo = this.baseMapper.selectVoByCode(code);
    if (vo == null) {
      return null;
    }

    DataTaskBranchSearchParamPo branchSearchParamPo = new DataTaskBranchSearchParamPo();
    branchSearchParamPo.setParentId(vo.getId());
    vo.setBranch(dataTaskBranchService.search(branchSearchParamPo));

    return vo;
  }

  /**
   * 分页查询数据事务
   * @param search 查询条件
   * @return 数据事务分页查询结果
   */
  @Override
  public Page<DataTaskItemVo> pageSearch(DataTaskSearchParamPo search) {
    search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
    Page<DataTaskItemVo> page =
      this.baseMapper.pageSearch(search.toPage(), search);
    // 填充子表对象
    final List<DataTaskItemVo> records = page.getRecords();
    for (DataTaskItemVo record : records) {
      DataTaskBranchSearchParamPo searchParamPo = new DataTaskBranchSearchParamPo();
      searchParamPo.setParentId(record.getId());
      searchParamPo.setType("sub");
      record.setChildren(branchService.search(searchParamPo));
    }
    return page;
  }

  /**
   * 列表查询数据事务
   * @param search 查询条件
   * @return 数据事务列表查询结果
   */
  @Override
  public List<DataTaskItemVo> search(DataTaskSearchParamPo search) {
    search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
    return this.baseMapper.search(search);
  }

  /**
   * 查询数据事务最后更新时间
   * @param search 查询条件
   * @return 数据事务最后更新时间
   */
  @Override
  public Date searchLastUpdateTime(DataTaskSearchParamPo search) {
    return this.baseMapper.searchLastUpdateTime(search);
  }

  @Override
  public List<DataTaskItemVo> getMainByFromTable(String fromTable, String objectName) {
    return this.baseMapper.selectMainByFromTable(fromTable, objectName);
  }

  @Override
  public List<DataTaskItemVo> getMainByToTable(String toTable, String objectName) {
    return this.baseMapper.selectMainByToTable(toTable, objectName);
  }
}
